package com.uinnova.product.eam.service.impl;

import com.binary.core.exception.BinaryException;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Lists;
import com.uinnova.product.eam.comm.model.es.EamCategory;
import com.uinnova.product.eam.comm.model.es.FolderPermission;
import com.uinnova.product.eam.comm.model.es.FolderPermissionManager;
import com.uinnova.product.eam.model.dto.FolderPermissionManagerVo;
import com.uinnova.product.eam.model.enums.CategoryTypeEnum;
import com.uinnova.product.eam.service.EamCategorySvc;
import com.uinnova.product.eam.service.IFolderPermissionManagerService;
import com.uinnova.product.eam.service.es.EamFolderPermissionManagerDao;
import com.uino.api.client.permission.IRoleApiSvc;
import com.uino.api.client.permission.IUserApiSvc;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.permission.base.SysRole;
import com.uino.bean.permission.business.UserInfo;
import com.uino.dao.util.ESUtil;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.script.Script;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 文件夹权限服务层
 *
 * @author lichong
 * @since 2022/6/30 10:36
 */
@Slf4j
@Service
public class FolderPermissionManagerService implements IFolderPermissionManagerService {

    @Autowired
    private EamFolderPermissionManagerDao folderPermissionManagerDao;
    @Autowired
    private EamCategorySvc eamCategorySvc;
    @Autowired
    private IRoleApiSvc roleApiSvc;

    @Autowired
    private IUserApiSvc userApiSvc;

    @Override
    public void saveFolderPermissions(FolderPermissionManagerVo folderPermissionManagerVo) {
        //先删除改文件夹中关于用户的权限
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.filter(QueryBuilders.termQuery("dirId", folderPermissionManagerVo.getDirId()));
        boolQueryBuilder.filter(QueryBuilders.termsQuery("roleId", folderPermissionManagerVo.getRoleIds()));
        folderPermissionManagerDao.deleteByQuery(boolQueryBuilder, true);
        //清除子文件权限
        deleteFolderPermissionManager(folderPermissionManagerVo.getDirId(),folderPermissionManagerVo.getRoleIds());

        //执行保存
        List<Long> roleIds = folderPermissionManagerVo.getRoleIds();
        BoolQueryBuilder roleQuery = QueryBuilders.boolQuery();
        roleQuery.filter(QueryBuilders.termsQuery("id",roleIds));
        List<SysRole> rolesByQuery = roleApiSvc.getRolesByQuery(roleQuery);
        Map<Long, String> roleNameMap = rolesByQuery.stream().collect(Collectors.toMap(SysRole::getId, SysRole::getRoleName));

        FolderPermissionManager folderPermissionManagerPrototype = new FolderPermissionManager();
        BeanUtils.copyProperties(folderPermissionManagerVo, folderPermissionManagerPrototype);
        //设置为非继承的权限
        folderPermissionManagerPrototype.setExtendPermission(Boolean.FALSE);
        EamCategory currentCategory = eamCategorySvc.getById(folderPermissionManagerPrototype.getDirId(), LibType.DESIGN);
        List<FolderPermissionManager> folderPermissionManagers = Lists.newArrayList();
        for (Long roleId : roleIds) {
            try {
                //原型模式复制对象
                FolderPermissionManager folderPermissionManager = (FolderPermissionManager) folderPermissionManagerPrototype.clone();
                folderPermissionManager.setRoleId(roleId);
                folderPermissionManager.setRoleName(roleNameMap.get(roleId));
                folderPermissionManager.setId(ESUtil.getUUID());
                this.setFolderPermissionIfNeed(currentCategory, folderPermissionManager);
                folderPermissionManagers.add(folderPermissionManager);
            } catch (CloneNotSupportedException e) {
                log.error("对象复制失败", e);
            }
        }
        //判断是否需要继承权限
        if (folderPermissionManagerVo.getFolderApplicationScope().getChildFolderAndFile()) {
            //若设置文件夹为继承权限需要将该文件夹下的权限设置继承
            List<EamCategory> childrenDirList = eamCategorySvc.findAllChildrenListNoAuth(folderPermissionManagerVo.getDirId());
            Set<Long> dirIds = childrenDirList.stream().map(EamCategory::getId).collect(Collectors.toSet());
            //查询出已经设置过权限的子文件夹
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.filter(QueryBuilders.termsQuery("dirId", dirIds));
            query.filter(QueryBuilders.termsQuery("roleId", roleIds));
            query.filter(QueryBuilders.termQuery("extendPermission", Boolean.FALSE));
            List<FolderPermissionManager> listByQueryScroll = folderPermissionManagerDao.getListByQueryScroll(query);
            HashBasedTable<Long, Long, FolderPermissionManager> tempTables = HashBasedTable.create();
            for (FolderPermissionManager folderPermissionManager : listByQueryScroll) {
                tempTables.put(folderPermissionManager.getRoleId(), folderPermissionManager.getDirId(), folderPermissionManager);
            }
            if (!CollectionUtils.isEmpty(childrenDirList)) {
                for (Long roleId : roleIds) {
                    for (EamCategory category : childrenDirList) {
                        FolderPermissionManager tempFolderPermissionManager = tempTables.get(roleId, category.getId());
                        if (tempFolderPermissionManager != null) {
                            //若该角色已经手动设置过改文件夹权限则不继承
                            continue;
                        }
                        FolderPermissionManager folderPermissionManager;
                        try {
                            folderPermissionManager = (FolderPermissionManager) folderPermissionManagerPrototype.clone();
                            folderPermissionManager.setRoleId(roleId);
                            folderPermissionManager.setRoleName(roleNameMap.get(roleId));
                            folderPermissionManager.setId(ESUtil.getUUID());
                            folderPermissionManager.setDirId(category.getId());
                            this.setFolderPermissionIfNeed(category, folderPermissionManager);
                            //子文件夹设置为继承权限
                            folderPermissionManager.setExtendPermission(Boolean.TRUE);
                            folderPermissionManagers.add(folderPermissionManager);
                        } catch (CloneNotSupportedException e) {
                            log.error("对象复制失败", e);
                        }
                    }
                }
            }
        }
        saveFolderPermissions(folderPermissionManagers);
    }

    @Override
    public void saveFolderPermissions(List<FolderPermissionManager> folderPermissionManagers) {
        int batchSize=5000;
        if(folderPermissionManagers.size()<batchSize){
            folderPermissionManagerDao.saveOrUpdateBatch(folderPermissionManagers);
        }else {
            for (int i = 1; i < Integer.MAX_VALUE; i++) {
                int from = (i - 1) * batchSize;
                if (from >= folderPermissionManagers.size()) {
                    break;
                }
                int to = Math.min((i - 1) * batchSize + batchSize, folderPermissionManagers.size());
                List<FolderPermissionManager> folderPermissionManagersBlock = folderPermissionManagers.subList(from, to);
                folderPermissionManagerDao.saveOrUpdateBatch(folderPermissionManagersBlock);
            }
        }
    }

    @Override
    public List<FolderPermissionManager> getFolderPermissionsByDirId(Long dirId) {
        EamCategory eamCategory = eamCategorySvc.getById(dirId, LibType.DESIGN);
        if (eamCategory == null || eamCategory.getDataStatus() != 1) {
            throw new BinaryException("操作的文件夹已删除,请刷新页面");
        }
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.filter(QueryBuilders.termQuery("dirId",dirId));

        List<FolderPermissionManager> retList = new ArrayList<>();
        // 增加判断，如果角色已经删除或名称发生变更，返回的权限列表中应该同步更新
        List<FolderPermissionManager> fpmList = folderPermissionManagerDao.getListByQuery(boolQueryBuilder);
        if(!fpmList.isEmpty()){
            // 批量查询出最新的角色信息
            List<Long> roleIds = new ArrayList<>();
            for(FolderPermissionManager fp : fpmList){
                roleIds.add(fp.getRoleId());
            }
            List<SysRole> roles = roleApiSvc.getRoleListByIds(roleIds,SysUtil.getCurrentUserInfo().getDomainId());
            if(!roles.isEmpty()) {
                Map<Long, SysRole> roleMap = new HashMap<>();
                for (SysRole role : roles) {
                    roleMap.put(role.getId(), role);
                }
                for (FolderPermissionManager fp : fpmList) {
                    if (roleMap.containsKey(fp.getRoleId())) {
                        // 如果角色未被删除，则更新角色名称至最新
                        fp.setRoleName(roleMap.get(fp.getRoleId()).getRoleName());
                        retList.add(fp);
                    } else {
                        // 删除文件夹权限列表中已删除的角色
                        BoolQueryBuilder deleteQuery = QueryBuilders.boolQuery();
                        deleteQuery.filter(QueryBuilders.termQuery("roleId", fp.getRoleId()));
                        deleteQuery.filter(QueryBuilders.termQuery("dirId", dirId));
                        folderPermissionManagerDao.deleteByQuery(deleteQuery, true);
                    }
                }
            }
        }
        return retList;
    }

    @Override
    public List<FolderPermissionManager> getNeedExtendFolderPermissionsByDirId(Long dirId) {
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.filter(QueryBuilders.termQuery("dirId",dirId));
        query.filter(QueryBuilders.termQuery("folderApplicationScope.childFolderAndFile",true));
        return folderPermissionManagerDao.getListByQuery(query);
    }

    @Override
    public void updateFolderPermissionManager(FolderPermissionManager folderPermissionManager) {
        //查询子文件夹
        List<EamCategory> childrenDirList = eamCategorySvc.findAllChildrenListNoAuth(folderPermissionManager.getDirId());
        //查询子文件夹中设置过权限的文件夹
        List<FolderPermissionManager> noExtendFolderPermissionManagerByUser =
                getNoExtendFolderPermissionManagerByUser(childrenDirList, folderPermissionManager.getRoleId());
        Set<Long> dirIds = noExtendFolderPermissionManagerByUser.stream().map(FolderPermissionManager::getDirId).collect(Collectors.toSet());
        //排除所有不是继承权限的文件夹
        childrenDirList.removeIf(d -> dirIds.contains(d.getId()));
        Set<Long> extendDirIds = childrenDirList.stream().map(EamCategory::getId).collect(Collectors.toSet());
        ArrayList<FolderPermissionManager> folderPermissionManagers = Lists.newArrayList();
        //清除该角色下子文件夹的权限
        BoolQueryBuilder deleteQuery = QueryBuilders.boolQuery();
        deleteQuery.filter(QueryBuilders.termQuery("roleId", folderPermissionManager.getRoleId()));
        deleteQuery.filter(QueryBuilders.termsQuery("dirId", extendDirIds));
        folderPermissionManagerDao.deleteByQuery(deleteQuery, true);
        if (folderPermissionManager.getFolderApplicationScope().getChildFolderAndFile()) {
            //若是继承则批量创建创建权限
            for (EamCategory category : childrenDirList) {
                if (dirIds.contains(category.getId())) {
                    continue;
                }
                try {
                    FolderPermissionManager clone = (FolderPermissionManager) folderPermissionManager.clone();
                    clone.setDirId(category.getId());
                    clone.setId(ESUtil.getUUID());
                    clone.setExtendPermission(Boolean.TRUE);
                    this.setFolderPermissionIfNeed(category, clone);
                    folderPermissionManagers.add(clone);
                } catch (CloneNotSupportedException e) {
                    log.error("复制权限对象失败", e);
                }
            }
        }
        EamCategory category = eamCategorySvc.getById(folderPermissionManager.getDirId(), LibType.DESIGN);
        folderPermissionManager.setExtendPermission(Boolean.FALSE);
        this.setFolderPermissionIfNeed(category, folderPermissionManager);
        folderPermissionManagers.add(folderPermissionManager);
        saveFolderPermissions(folderPermissionManagers);
    }

    @Override
    public void deleteFolderPermissions(Long id) {
        //删除子文件夹权限
        FolderPermissionManager folderPermissionManager = folderPermissionManagerDao.getById(id);
        Long dirId = folderPermissionManager.getDirId();
        List<EamCategory> childrenDirList = eamCategorySvc.findAllChildrenListNoAuth(dirId);
        Set<Long> childIds = childrenDirList.stream().map(EamCategory::getId).collect(Collectors.toSet());
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.filter(QueryBuilders.termsQuery("dirId",childIds));
        query.filter(QueryBuilders.termQuery("roleId",folderPermissionManager.getRoleId()));
        query.filter(QueryBuilders.termQuery("extendPermission",Boolean.TRUE));
        folderPermissionManagerDao.deleteByQuery(query,true);
        //删除文件夹权限
        folderPermissionManagerDao.deleteById(id);
    }

    @Override
    public void deleteFolderPermissionsIncludeChildByDirId(Long dirId) {
        List<EamCategory> childrenDirList = eamCategorySvc.findAllChildrenListNoAuth(dirId);
        Set<Long> childIds = childrenDirList.stream().map(EamCategory::getId).collect(Collectors.toSet());
        childIds.add(dirId);
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.filter(QueryBuilders.termsQuery("dirId",childIds));
        folderPermissionManagerDao.deleteByQuery(query,true);
    }

    @Override
    public List<FolderPermissionManager> getFolderPermissionsByLoginCodeAndDirId(String loginCode, List<Long> dirIds) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.filter(QueryBuilders.termQuery("loginCode.keyword", loginCode));
        boolQueryBuilder.filter(QueryBuilders.termsQuery("dirId", dirIds));
        BoolQueryBuilder readQuery = QueryBuilders.boolQuery();
        readQuery.should(QueryBuilders.termQuery("modelPermission.read", Boolean.TRUE));
        readQuery.should(QueryBuilders.termQuery("folderPermissions.read",Boolean.TRUE));
        boolQueryBuilder.filter(readQuery);
        return folderPermissionManagerDao.getListByQuery(boolQueryBuilder);
    }

    @Override
    public List<FolderPermissionManager> getFolderPermissionsByDirTypeAndUser(String loginCode, int dirType) {
        Set<Long> userRoles = getUserRoles();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.filter(QueryBuilders.termsQuery("roleId", userRoles));
        boolQueryBuilder.filter(QueryBuilders.termQuery("dirType", dirType));
        boolQueryBuilder.filter(QueryBuilders.termsQuery("folderApplicationScope.currentFolder", Boolean.TRUE));
        BoolQueryBuilder readQuery = QueryBuilders.boolQuery();
        readQuery.should(QueryBuilders.termQuery("modelPermission.read", Boolean.TRUE));
        readQuery.should(QueryBuilders.termQuery("folderPermissions.read",Boolean.TRUE));
        boolQueryBuilder.filter(readQuery);
        return folderPermissionManagerDao.getListByQuery(boolQueryBuilder);
    }

    @Override
    public void moveDirChangeFolderPermissions(List<Long> dirIds, Long parentDirId) {
        //清除该用户下子文件夹的权限
        List<EamCategory> childrenDirList = Lists.newArrayList();
        for (Long dirId : dirIds) {
            List<EamCategory> childrenDirList1 = eamCategorySvc.findAllChildrenListNoAuth(dirId);
            childrenDirList.addAll(childrenDirList1);
        }
        if(CollectionUtils.isEmpty(childrenDirList)){
            return;
        }
        Set<Long> allDirIds = childrenDirList.stream().map(EamCategory::getId).collect(Collectors.toSet());
        allDirIds.addAll(dirIds);
        BoolQueryBuilder deleteQuery = QueryBuilders.boolQuery();
        deleteQuery.filter(QueryBuilders.termsQuery("dirId", allDirIds));
        folderPermissionManagerDao.deleteByQuery(deleteQuery, true);

        //重建文件夹权限
        //1、获取需要集成的文件夹权限
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.filter(QueryBuilders.termQuery("dirId",parentDirId));
        query.filter(QueryBuilders.termQuery("folderApplicationScope.childFolderAndFile",Boolean.TRUE));
        List<FolderPermissionManager> needExtendPermission = folderPermissionManagerDao.getListByQueryScroll(query);
        ArrayList<FolderPermissionManager> folderPermissionManagers = Lists.newArrayList();
        for (FolderPermissionManager folderPermissionManager : needExtendPermission) {
            //根据文件夹新建
            for (EamCategory eamCategory : childrenDirList) {
                try {
                    FolderPermissionManager clone = (FolderPermissionManager)folderPermissionManager.clone();
                    clone.setDirId(eamCategory.getId());
                    clone.setExtendPermission(Boolean.TRUE);
                    clone.setId(ESUtil.getUUID());
                    this.setFolderPermissionIfNeed(eamCategory, clone);
                    folderPermissionManagers.add(clone);
                } catch (CloneNotSupportedException e) {
                    log.error("复制对象出错",e);
                }
            }
        }
        saveFolderPermissions(folderPermissionManagers);
    }

    @Override
    public List<FolderPermissionManager> getFolderPermissionByRoleIds(Set<Long> roleIds) {
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.filter(QueryBuilders.termsQuery("roleId",roleIds));
        query.filter(QueryBuilders.termQuery("folderApplicationScope.currentFolder",Boolean.TRUE));
        BoolQueryBuilder readQuery = QueryBuilders.boolQuery();
        readQuery.should(QueryBuilders.termQuery("modelPermission.read", Boolean.TRUE));
        readQuery.should(QueryBuilders.termQuery("folderPermissions.read",Boolean.TRUE));
        query.filter(readQuery);
        return folderPermissionManagerDao.getListByQueryScroll(query);
    }

    @Override
    public List<FolderPermissionManager> getFolderPermissionByDirIds(List<Long> dirIds) {
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.filter(QueryBuilders.termsQuery("dirId",dirIds));
        return folderPermissionManagerDao.getListByQueryScroll(query);
    }

    /**
     * 获取用户的不是继承权限的文件夹权限
     * @param childrenDirList
     * @param roleId
     * @return
     */
    private List<FolderPermissionManager> getNoExtendFolderPermissionManagerByUser(List<EamCategory> childrenDirList
            , Long roleId) {
        Set<Long> dirIds = childrenDirList.stream().map(EamCategory::getId).collect(Collectors.toSet());
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.filter(QueryBuilders.termsQuery("dirId", dirIds));
        query.filter(QueryBuilders.termQuery("roleId", roleId));
        query.filter(QueryBuilders.termQuery("extendPermission", Boolean.FALSE));
        return folderPermissionManagerDao.getListByQueryScroll(query);
    }

    /**
     * 删除父文件夹下用户继承的所有权限
     * @param dirParentId
     * @param roleIds
     */
    private void deleteFolderPermissionManager(Long dirParentId,List<Long> roleIds){
        //查询子文件夹
        List<EamCategory> childrenDirList = eamCategorySvc.findAllChildrenListNoAuth(dirParentId);
        Set<Long> dirIds = childrenDirList.stream().map(EamCategory::getId).collect(Collectors.toSet());
        //清除该用户下子文件夹的权限
        BoolQueryBuilder deleteQuery = QueryBuilders.boolQuery();
        deleteQuery.filter(QueryBuilders.termsQuery("roleId", roleIds));
        deleteQuery.filter(QueryBuilders.termsQuery("dirId", dirIds));
        deleteQuery.filter(QueryBuilders.termsQuery("extendPermission", Boolean.TRUE));
        folderPermissionManagerDao.deleteByQuery(deleteQuery, true);
    }

    private Set<Long> getUserRoles(){
        UserInfo user = userApiSvc.getUserInfoById(SysUtil.getCurrentUserInfo().getId());
        Set<Long> roleIds = user.getRoles().stream().map(SysRole::getId).collect(Collectors.toSet());
        return roleIds;
    }

    /**
     * 模型无文件夹权限
     * @param category
     * @param folderPermissionManager
     */
    @Override
    public void setFolderPermissionIfNeed(EamCategory category, FolderPermissionManager folderPermissionManager) {
        if (category.getType() == CategoryTypeEnum.MODEL_ROOT.val()) {
            folderPermissionManager.setFolderPermissions(new FolderPermission());
        }
    }

    @Override
    public List<FolderPermissionManager> queryEmptyModelPermission() {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.filter(QueryBuilders.scriptQuery(new Script("doc['modelPermission.read'].length==0")));
        return folderPermissionManagerDao.getListByQuery(boolQueryBuilder);
    }

    @Override
    public void deleteFolderPermissionsByDirIds(List<Long> dirIds) {
        if (CollectionUtils.isEmpty(dirIds)) {
            return;
        }
        BoolQueryBuilder deleteQuery = QueryBuilders.boolQuery();
        deleteQuery.filter(QueryBuilders.termsQuery("dirId", dirIds));
        folderPermissionManagerDao.deleteByQuery(deleteQuery, true);
    }

    @Override
    public void deleteFolderPermissionsByIds(Set<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        BoolQueryBuilder deleteQuery = QueryBuilders.boolQuery();
        deleteQuery.must(QueryBuilders.termsQuery("id", ids));
        folderPermissionManagerDao.deleteByQuery(deleteQuery, true);
    }
}
